认识React
准备
babel-cli
写 React 有两种方式:
通过 jsx
通过 js
jsx 写起来感觉会更方便,但就需要用babel进行编译了。
用babel编译 React 的 jsx 有三种方法( 关于 babel 的具体使用方法好像还是有很多疑问的。以后会再折腾下)
Node.js运行时编译
浏览器(客户端)运行时编译
这样我打算用 babel-cli 进行实时编译,会比较方便。
安装
将工具全局安装了:
$ [sudo] npm i -gd babel-cli
插件单独安装:
$ npm i -d babel-preset-react
使用
ps:我这里之前说错了
这里有个坑,在 windows 系统的话,可以直接
babel --presets react
,但在 os x 会报错,要直接直接指定绝对路径/usr/local/lib/node_modules/babel-preset-react
,
babel-cli 的 --presets
参数是会向父级目录寻找 node_module
里面的 babel-prest-xxx
插件,如果没有找到,就会相对于当前目录路径寻找插件。
除此之外,你也可以写成绝对路径的插件地址。
在单独安装了插件后,你就可以不用写绝对路径地址了。
$ babel --presets react jsx --watch --out-dir build
如果不想每次敲命令,可以在项目目录(就是敲babel命令的目录)中新建一个.babelrc
文件:
{
"presets": ["react"]
}
这样敲命令的时候就可以不要带上--presets
参数了。
$ babel jsx --watch --out-dir build
jsx
是需要编译的文件夹,build
是编译后产出的文件夹。
使用 React v0.14.x
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="JBody"></div>
<!-- react 核心 js-->
<script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<!-- 编译后的 jsx 引用 -->
<script src="./build/test.js"></script>
</body>
</html>
ps:官网的示例中,react 核心 js 是放在 <head></head>
标签里面的,因为这样的话,在最后渲染 DOM 的时候就可以直接选择在<body></body>
标签里面了。不然就要像我这样,在<body></body>
标签里面给个节点供 react 进行渲染。
组件
组件概念
先来个 Input
组件。
var Input = React.createClass({
render: function(){
return(
<label for={this.props.id}>
<i>四月是誰的谎言</i>
<input id={this.props.id} value type="text"/>
</label>
)
}
})
通过 Reac.createClass
创建的就是组件了。rander
函数 return 的东西可以称作模板。
每个组件都是一个树形结构(有父级节点和子节点,也就是不能同时存在两个顶级节点,react 是不允许的)。
在 React 里面就是一个个组件组成的,组合在一起就也是一个树形结构。
组件的渲染
ReactDOM.render(
<Input />,
document.getElementById('JBody')
);
调用 ReactDOM.render
方法进行渲染,第一个参数是组件了,第二个是装载该组件的容器。
同样的,第一个参数只支持树形结构,也就是只有一个顶级节点。
组件的两个重要对象
props
props
就是一个组件模板的数据对象。里面储存着静态的数据,也就是只在调用时赋值,之后如果有数据的改变也不会再次进行模板的渲染了。
props 的使用:
var Input = React.createClass({
render: function(){
return(
// 使用 props
<label for={this.props.iid}>
<i>四月是誰的谎言</i>
<input id={this.props.iid} value type="text"/>
</label>
)
}
})
ReactDOM.render(
<Input iid="JWho"/>, // 赋值 props
document.getElementById('JBody')
)
state
state
跟 props
是相对的。state
是动态的数据对象,也就是可以任何时候进行赋值,之后如果有数据的改变会进行模板的再次渲染(这里渲染的话就涉及到了 react 的 virtual DOM)了。
state 的使用:
这里定义个列表组件Items
var Items = React.createClass({
getInitialState: function() {
return {
data: [12312,1231,45,51512312,2131]
}
},
render: function(){
return(
<ul>
{this.state.data.map((value,i) => {
return <li>{value}</li>
})}
</ul>
)
}
})
getInitialState
用来定义 state
的初始数据。
还可以在组件的生命周期函数里可以从服务器获取数据然后通过调用 this.setState
方法进行对 state
赋值。
this.setState
就是那个 可以任何时候进行赋值
的方法了。
改造下 Input
组件,state
示例:
var Input = React.createClass({
getInitialState: function() {
return {
who: '我'
}
},
change: function(e){
let target = e.target
this.setState({
who : target.value
})
},
render: function(){
// 使用 props
return(
<div>
<label for={this.props.iid}>
<i>四月是誰的谎言?</i>
<input id={this.props.iid} onChange={this.change} type="text"/>
</label>
<p>{`四月是${this.state.who}的谎言`}</p>
</div>
)
}
})
ReactDOM.render(
<Input />,
document.getElementById('JBody')
)
这里进行了事件 onChange
的调用,当触发 change 事件的时候,state
就会进行改版了。
关于时间系统的文档:
组件的文档
组件的通信
react 的核心中是没有关于两个不同组件之间的相互通信的方法的,但是官方给出了一种flux的通信概念、flux中文文档,然而社区似乎并不觉得这一套方案很好用,于是有了另一种通信概念redux。
我对通信的理解
react 的组件化概念是很明确的,但对于组件之间通信可以说是基本没有。
在我的理解里面,组件的通信可以更明确地理解为数据的通信,因为数据和组件渲染分离是很重要的。
一个负责不同组件渲染的组件容器,会自行判断数据的状态从而进行最终组件的渲染。这样就能做到数据和组件渲染的分离了。这个概念就是Container Components
、Leveling Up With React: Container Components
在其他的一些mv*
框架里面,每个组件都有自己的通信接口,从而可以在A组件里面跟B组件进行数据通信,但 react 却是没有这样的接口,从而我们就需要一个借助一些通信工具,比如手机、电脑什么的。
没错,我们可以把 Flux、Redux 这些工具当成通信工具来看待,如果你喜欢,你完全可以用写信的方式(自己写一个观察者模式的函数)。当A组件的数据要与B组件的数据进行打电话的时候,拨通B组件的电话号码,然后把数据告诉B组件就可以了。数据会在运营商的系统里面走一遭,然后传到B组件那里的。
通信场景
例如登录。
在一个页面上,如果存在两个或两个以上需要获取登录信息的组件,就将需要进行通信了。
当一个组件进行了登录之后,应该通知其他需要更新登录信息的组件。这样就产生了通信的需求了。
组件的抽象与封装
这块到没有太多的实践,组件的拆分、定义是一门深思熟虑的艺术。等有实践经验找到门路之后再做补充。
但这块可能是很多人会忽略的,优秀的抽象封装首先应该是针对方便开发和维护的,其次才是复用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。